package com.zws.cucumber.bootstrap;

import com.zws.cucumber.bootstrap.cass.*;
import com.zws.cucumber.util.ZwsJavaHomeUtil;
import com.zws.cucumber.util.cass.ZwsCassContext;
import com.zws.cucumber.util.cass.backdoor.ZwsCassBackdoorClient;
import com.zws.cucumber.util.colorfullog.ZwsColorfulLogger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * author: zws
 */
public class ZwsCassBootstrap {

    private static final Logger logger = ZwsColorfulLogger.of(LoggerFactory.getLogger(ZwsCassBootstrap.class));

    public static final int CASSANDRA_PORT = 9042;
    public static final int BACKDOOR_PORT = 19042;

    public static final String FLAG_LOCAL_CASSANDRA = "useLocalCassandra";
    public static final String FLAG_STANDALONE_CASSANDRA_WSL = "useStandaloneCassandraWsl";
    public static final String FLAG_STANDALONE_CASSANDRA_JENKINS = "useStandaloneCassandraJenkins";

    public static final String jdk8Home = ZwsJavaHomeUtil.findJdk8Home();

    public static volatile ZwsCassBackdoorClient cassBackdoorClient;

    private static boolean needUseLocalCassandra() {
        System.out.printf("System.getProperty(\"useLocalCassandra\"): %s, use local? %s\n",
                System.getProperties().get(FLAG_LOCAL_CASSANDRA),
                System.getProperties().containsKey(FLAG_LOCAL_CASSANDRA));
        return System.getProperties().containsKey(FLAG_LOCAL_CASSANDRA);
    }

    public static boolean usingStandaloneCassandra() {
        return SystemUtils.IS_OS_WINDOWS
                && StringUtils.isNotBlank(jdk8Home)
                && !needUseLocalCassandra();
    }

    public static boolean usingStandaloneCassandraWsl() {
        return StringUtils.isNotBlank(jdk8Home)
                && System.getProperties().containsKey(FLAG_STANDALONE_CASSANDRA_WSL);
    }

    public static boolean usingStandaloneCassandraJenkins() {
        return StringUtils.isNotBlank(jdk8Home)
                && System.getProperties().containsKey(FLAG_STANDALONE_CASSANDRA_JENKINS);
    }

    public static ZwsCassContext getCassandraServer(String creationScriptPath, String dataScriptPath, String keyspace) {
        try {
            ZwsBootstrapUtil.println("creationScriptPath: " + creationScriptPath);
            ZwsBootstrapUtil.println("dataScriptPath: " + dataScriptPath);
            accelerateCqlSessionInitialization();
            return getBootstrap().boot(creationScriptPath, dataScriptPath, keyspace);

        } catch (Exception e) {
            ZwsBootstrapUtil.println("boot cassandra failed, exit!");
            e.printStackTrace();
            System.exit(1);
            return null;
        }
    }

    private static void accelerateCqlSessionInitialization() {
        // accelerate the speed of initialization of CqlSession
        // https://github.com/quarkusio/quarkus/issues/8822
        long timestamp = System.currentTimeMillis();
        final byte[] machineIdBytes = new byte[8];
        java.util.concurrent.ThreadLocalRandom.current().nextBytes(machineIdBytes);
        final String machineId = io.netty.util.internal.MacAddressUtil.formatAddress(machineIdBytes);
        System.setProperty("io.netty.machineId", machineId);
        ZwsBootstrapUtil.printf("accelerate CqlSession initialization by setting property \"io.netty.machineId\", elapsed: %dms\n",
            System.currentTimeMillis() - timestamp);
    }

    public static com.zws.cucumber.bootstrap.cass.ZwsCassBootstrap getBootstrap() {
        logger.info("jdk8Home: {}", jdk8Home);
        logger.info("System.getProperties().containsKey(FLAG_STANDALONE_CASSANDRA_WSL): {}", System.getProperties().containsKey(FLAG_STANDALONE_CASSANDRA_WSL));
        if (usingStandaloneCassandraWsl()) {
            logger.info("using standalone cassandra wsl");
            return new ZwsStandaloneCassWslBootstrap();

        } else if (usingStandaloneCassandraJenkins()) {
            logger.info("using standalone cassandra jenkins");
            return new ZwsStandaloneCassJenkinsBootstrap();

        } else if (usingStandaloneCassandra()) {
            logger.info("using standalone cassandra windows");
            return new ZwsStandaloneCassWindowsBootstrap();

        } else {
            logger.info("using local cassandra");
            return new ZwsLocalCassBootstrap();
        }
    }

    public static Triple<Boolean, Integer, Pair<Integer, Integer>> getStandaloneCassandra() throws Exception {
        com.zws.cucumber.bootstrap.cass.ZwsCassBootstrap bootstrap = new ZwsStandaloneCassWindowsBootstrap();
        bootstrap.prepareAndStartCassandra();
        return Triple.of(true, 0, Pair.of(bootstrap.getCassandraPort(), bootstrap.getBackdoorPort()));
    }
}
